﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Diagnostics;

namespace CS544_Protocol_Client
{

    /**
    * Author: Kelly Page, Brian Driscoll
    * Date Created: 2/23/2011
    * Date Updated: 3/02/2011
    * 
    * Purpose: Main chat client. Sends and recieves messages, maintains connection.
    * 
    **/

    public delegate void MessageReceivedHandler(object sender, MessageEventArgs e);


    public class ChatClient
    {

        //Constructor
        public ChatClient()
        {
            IpAddress = System.Configuration.ConfigurationManager.AppSettings["host"];
            PortNumber = Convert.ToInt32(System.Configuration.ConfigurationManager.AppSettings["port"]);
            partial = String.Empty;
            msgID = 0;
        }

        //Constructor to override IpAddress and PortNumber with custom values
        public ChatClient(string ip, int port) : this()
        {
            IpAddress = ip;
            PortNumber = port;
        }

        //initializes a connection
        public void Connect()
        {
            try
            {
                client = new TcpClient(IpAddress, PortNumber);
                data = new byte[client.ReceiveBufferSize];

                //start reading from the server
                client.GetStream().BeginRead(data, 0, Convert.ToInt32(client.ReceiveBufferSize), ReceiveMessage, null);
            }
            catch (Exception e)
            {
                throw new Exception(String.Format("An error occurred while trying to connect to {0}", IpAddress),e);
            }
        }

        public void Disconnect()
        {
            try
            {
                if (client != null)
                {
                    client.Close();
                }
            }
            catch (Exception e)
            {
                throw new Exception(String.Format("An error occurred while trying to disconnect from {0}", IpAddress), e);
            }
            finally
            {
                client = null;
            }
        }

        public void SendMessage(Message msg)
        {

            Debug.WriteLine("Sending Message " + msg.Type);
            try
            {
                NetworkStream ns;
                lock (client.GetStream())
                {
                    ns = client.GetStream();
                    byte[] msgToSend = Message.Serialize(msg);
                    for(int i = 0;i<msgToSend.Length;i++)
                    {
                        Debug.Write("[" + msgToSend[i] + "]");
                    }
                    ns.Write(msgToSend, 0, msgToSend.Length);
                    ns.Flush();
                }
            }
            catch (Exception e)
            {
                throw new Exception("An error occurred while trying to send a message", e);
            }
        }

        public void ReceiveMessage(IAsyncResult result)
        {
            try
            {
                int bytesRead;
                bytesRead = client!=null ? client.GetStream().EndRead(result) : 0;
                if (bytesRead < 1)
                    return;

                string messageReceived;
                int i = 0;
                int start = 0;

                while (true)
                {
                    //don't scan beyond what's been read
                    if (i + 1 > bytesRead)
                        break;

                    if (i>4 && data[i] == Message.ETX)
                    {
                        messageReceived = partial + Encoding.ASCII.GetString(data, start, i - start) + Message.ETX;

                       
                        Message msg = Message.Deserialize(messageReceived);
                        Debug.WriteLine("Received Message " + msg.Type);
                        OnMessageReceived(new MessageEventArgs(msg));

                        

                        start = i + 1;
                    }
                    i += 1;
                }

                //we may have only received a partial message,
                //so we need to be able to deal with that.
                if (start != i)
                {
                    partial = Encoding.ASCII.GetString(data, start, i - start);
                }

                //keep reading from the server
                if (client != null)
                {
                    client.GetStream().BeginRead(data, 0, Convert.ToInt32(client.ReceiveBufferSize), ReceiveMessage, null);
                }
            }
            catch (Exception e)
            {
                
                throw new Exception("An error occurred while receiving a message", e);
                
            }
        }

        private void OnMessageReceived(MessageEventArgs args)
        {
            if (MessageReceived != null)
                MessageReceived(this, args);
        }



        
        public event MessageReceivedHandler MessageReceived;

        public State ClientState { get; private set; }//holds state information
            

        private string IpAddress; //holds server address
        private int PortNumber; //holds server port
        private byte[] data; //stores raw data coming over the wire
        private string partial; //stores a partial message.
        private TcpClient client; //client tcp connection
        private int msgID; //an auto-incrementing message ID.
        public int MessageID
        {
            get
            {
                msgID++;
                return msgID;
            }
        }


    }

}